from fastapi import FastAPI, UploadFile, File, HTTPException, Form, Depends
from fastapi.responses import StreamingResponse
from fastapi.middleware.cors import CORSMiddleware
from minio import Minio
from minio.error import S3Error
import io
from starlette.background import BackgroundTask
import urllib.parse
from urllib.parse import unquote

# from database import Base, engine, SessionLocal, FileMetadata
from sqlalchemy.orm import Session
from sqlalchemy import or_

app = FastAPI(title="MinIO File API")

# Base.metadata.create_all(bind=engine)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3001"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 初始化 MinIO 客户端
minio_client = Minio(
    "localhost:9000",
    access_key="admin",
    secret_key="password",
    secure=False
)

BUCKET_NAME = "my-data"

@app.post("/upload/")
async def upload_file(username: str = Form(...), file: UploadFile = File(...)):
    if not username or not file.filename:
        raise HTTPException(status_code=400, detail="Missing username or filename")

    # 构造带用户前缀的对象名：username/filename
    object_name = f"{username}/{file.filename}"

    try:
        # 读取文件内容
        content = await file.read()
        stream = io.BytesIO(content)

        minio_client.put_object(
            BUCKET_NAME,
            object_name,
            data=stream,
            length=len(content),
            content_type=file.content_type or "application/octet-stream"
        )

        return {"message": "Upload successful", "filename": file.filename, "username": username}
    except S3Error as e:
        raise HTTPException(status_code=500, detail=f"S3 Error: {str(e)}")


@app.post("/upload-folder/")
async def upload_folder_file(
    username: str = Form(...),
    file_path: str = Form(...),
    file: UploadFile = File(...)
):
    if not username or not file_path:
        raise HTTPException(status_code=400, detail="Missing username or file path")

    # 构造对象名：username/file_path（如：alice/project/src/index.js）
    object_name = f"{username}/{file_path}"

    try:
        content = await file.read()
        stream = io.BytesIO(content)

        minio_client.put_object(
            BUCKET_NAME,
            object_name,
            data=stream,
            length=len(content),
            content_type=file.content_type or "application/octet-stream"
        )
        return {"message": "File uploaded", "path": object_name}
    except S3Error as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.get("/download/{username}/{filename}")
async def download_file(username: str, filename: str):
    if not username or not filename:
        raise HTTPException(status_code=400, detail="Missing username or filename")

    # 重建完整对象名
    object_name = f"{username}/{filename}"
    decoded_object_name = urllib.parse.unquote(object_name)

    try:
        response_data = minio_client.get_object(BUCKET_NAME, decoded_object_name)

        headers = {
            "Content-Disposition": f"attachment; filename*=UTF-8''{urllib.parse.quote(filename)}",
            "Content-Type": "application/octet-stream",
        }

        return StreamingResponse(
            response_data.stream(32 * 1024),
            media_type="application/octet-stream",
            headers=headers,
            background=BackgroundTask(response_data.close)
        )
    except S3Error as e:
        if e.code == "NoSuchKey":
            raise HTTPException(status_code=404, detail="File not found")
        else:
            raise HTTPException(status_code=500, detail=str(e))
    

@app.get("/files/{username}/{path:path}")
async def list_files(username: str, path: str = ""):
    if not username or '/' in username:
        raise HTTPException(status_code=400, detail="Invalid username")

    decoded_path = unquote(path).strip("/") if path else ""
    prefix = f"{username}/"
    if decoded_path:
        prefix += f"{decoded_path}/"

    try:
        objects = minio_client.list_objects(BUCKET_NAME, prefix=prefix, recursive=True)

        folders = set()
        files = []

        for obj in objects:
            # 去掉前缀，得到相对路径：如 project/src/index.js → src/index.js
            if obj.object_name.startswith(prefix):
                relative_path = obj.object_name[len(prefix):]
            else:
                continue

            if not relative_path:
                continue

            # 分割路径
            parts = relative_path.split('/', 1)  # 只分割一次：["src", "index.js"] 或 ["README.md"]

            if len(parts) == 0:
                continue

            first_part = parts[0]
            if not first_part:
                continue

            # 如果还有剩余路径，则 first_part 是个文件夹
            if len(parts) > 1:
                folders.add(first_part + "/")  # 添加文件夹
            else:
                # 是文件
                files.append({
                    "name": first_part,
                    "original_path": relative_path,  # 保留完整相对路径用于下载
                    "size": obj.size or 0,
                    "last_modified": obj.last_modified.isoformat() if obj.last_modified else None
                })

        # 去重排序
        folder_list = sorted([{"name": f, "is_dir": True} for f in folders])
        file_list = sorted(files, key=lambda x: x["name"])

        return {
            "current_path": decoded_path,
            "folders": folder_list,
            "files": file_list,
            "username": username
        }

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))